﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Diagnostics;
using System.Globalization;
using System.Threading;


namespace ComSatTranslator
{
    /// <summary>
    /// 
    /// </summary>
    /// <example>
    ///  AA -  Número de Reporte (00..99)
    ///  BBBBBB -  Fecha
    ///  CCCCCC -  Hora
    ///  DDDDDDDD - Latitud
    ///  EEEEEEEEE - Longitud
    ///  FFF - Velocidad
    ///  GGG - Rumbo
    ///  HH - Estado de las Entradas Digitales IGN+IN  (MSB ... LSB) en Hexadecimal (MSB corresponde al estado de IGN)
    ///  II - Estado de las Salidas Digitales XP (MSB ... LSB) en Hexadecimal
    ///  JJJ - Tensión de batería (en decimas de V)
    ///  KKKKKKKK - Contador metros de odómetro total (en hexa)
    ///  L - Estado de power GPS: ///  0=Off ///1=On
    ///M
    /// GPS Fix Mode:
    ///Configurable por comando CXCQ:
    ///2=2D FIX
    ///3=3D FIX
    ///NN
    /// PDOP (parte entera en decimal)
    ///Configurable por comando CXCQ:
    ///PDOP <= 50
    ///OO
    /// Cantidad de satélites (en decimal)

    ///Configurable por comando CXCQ:

    ///3<= Qty SAT <=24

    ///PPPP
    /// Cantidad de segundos desde la última posición correcta
    ///(en Hexadecimal).
    ///Q
    /// Estado de power MODEM CELULAR:
    ///0=Off
    ///1=On
    ///R
    /// Estado de registro GSM:
    ///0=Not registered, ME is not currently searching a new operador to register to
    ///1=Registered, home network
    ///2=Not registered, but ME is currently searching a new operator to register to
    ///3=Registration denied
    ///4=Unknown
    ///5=Registered, roaming
    ///SS
    /// Nivel de señal CEL (CSQ)
    ///99= Open / No Signal
    ///0-30 =  Level
    ///
    ///T...
    /// Mensaje de Texto (opcional, hasta 50 caracteres)
    ///UUUU
    /// Número de Mensaje (en Hexadecimal)
    /// 
    /// >RCQ16011112152340-3303881-06888087006107FF00 135 01920A6A 1 3 01 10 0000 1 2 31;#05D0;ID=1017;*05<
    /// </example>
    public class ComSatTranslator
    {
        #region Fields
        private DateTime _FechaHora;
        private string _Latitud;
        private string _Longitud;
        private string _Velocidad;
        private string _Reporte;
        private string _Rumbo;
        private string _EstadoEntradasDigitales;
        private string _EstadoSalidasDigitales;
        private string _TensionDeBateria;
        private string _Odometro;
        private string _EstadoPowerGPS;
        private string _GPSFixMode;
        private string _PDOP;
        private string _SatelitesDisponibles;
        private string _SegundosUltimaPosicionOk;
        private string _EstadoPowerModemCelular;
        private string _EstadoRegistroGSM;
        private string _NivelDeSeñal;
        private string _IdMovil;

        private int _Stan;

        private static int Stan = 0;
        private static object Lock = new Object();
        #endregion

        #region Constructor
        private ComSatTranslator(int stanParam)
        {
            CultureInfo culture = new CultureInfo("en-US");
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;

            _Stan = stanParam;
            _FechaHora = DateTime.UtcNow;
        }
        #endregion

        public static ComSatTranslator GetNewInstance()
        {
            lock (Lock)
            {
                return new ComSatTranslator(ComSatTranslator.Stan++);
            }
        }

        public string Serialize()
        {
            if (String.IsNullOrEmpty(_Latitud)) throw new ApplicationException("No esta configurado el valor Latitud, que es obligatorio");
            if (String.IsNullOrEmpty(_Longitud)) throw new ApplicationException("No esta configurado el valor Longitud, que es obligatorio");
            if (String.IsNullOrEmpty(_IdMovil)) throw new ApplicationException("No esta configurado el valor IdMovil, que es obligatorio");

            string result =
                    ">RCQ"
                    + (String.IsNullOrEmpty(_Reporte) ? "00" : _Reporte)
                    + _FechaHora.ToString("ddMMyyHHmmss")
                    + _Latitud
                    + _Longitud
                    + (String.IsNullOrEmpty(_Velocidad) ? "000" : _Velocidad)
                    + (String.IsNullOrEmpty(_Rumbo) ? "000" : _Rumbo)
                    + (String.IsNullOrEmpty(_EstadoEntradasDigitales) ? "FF" : _EstadoEntradasDigitales.PadLeft(2, '0').Substring(0, 2))
                    + (String.IsNullOrEmpty(_EstadoSalidasDigitales) ? "00" : _EstadoSalidasDigitales.PadLeft(2, '0').Substring(0, 2))
                    + (String.IsNullOrEmpty(_TensionDeBateria) ? "909" : _TensionDeBateria.PadLeft(3, '0').Substring(0, 3))
                    + (String.IsNullOrEmpty(_Odometro) ? "00000000" : _Odometro.PadLeft(8, '0').Substring(0, 8))
                    + (String.IsNullOrEmpty(_EstadoPowerGPS) ? "1" : _EstadoPowerGPS)
                    + (String.IsNullOrEmpty(_GPSFixMode) ? "2" : _GPSFixMode)
                    + (String.IsNullOrEmpty(_PDOP) ? "00" : _PDOP)
                    + (String.IsNullOrEmpty(_SatelitesDisponibles) ? "03" : _SatelitesDisponibles)
                    + (String.IsNullOrEmpty(_SegundosUltimaPosicionOk) ? "0000" : _SegundosUltimaPosicionOk)
                    + (String.IsNullOrEmpty(_EstadoPowerModemCelular) ? "1" : _EstadoPowerModemCelular)
                    + (String.IsNullOrEmpty(_EstadoRegistroGSM) ? "1" : _EstadoRegistroGSM)
                    + (String.IsNullOrEmpty(_NivelDeSeñal) ? "99" : _NivelDeSeñal)
                    + ";#" + _Stan.ToString("X4")
                    + ";ID=" + _IdMovil
                    + ";*";



            return result + Crc(result) + "<";


        }
        /// <summary>
        /// Calcula el CRC haciendo XOR de todos los bytes incluyendo el '*'
        /// </summary>
        /// <param name="result">Trama a realizar el CRC</param>
        /// <returns>Valor de CRC</returns>
        private string Crc(string result)
        {
            byte value = 0x00;

            Debug.WriteLine("RESULT: "+result);

            foreach (byte b in Encoding.Default.GetBytes(result))
            {
                //CLogger.Append("[] ", CLogger.INFO);
                Debug.WriteLine("VALUE: "+value.ToString("X2") );

                value ^= b;
            }

            return value.ToString("X2");
        }

        #region Properties
        public string CurrentStan
        {
            get
            {
                return _Stan.ToString("X4");
            }
#if DEBUG == true
            set
            {
                _Stan = Convert.ToInt32(value, 16);
            }
#endif
        }
        /// <summary>
        /// Valor de Id de Movil - OBLIGATORIO
        /// </summary>
        public string IdMovil { set { _IdMovil = value.PadLeft(4, '0').Substring(0, 4); } }

        /// <summary>
        /// Valor de Latitud de Movil - OBLIGATORIO
        /// DDDDDDDD - Latitud
        /// </summary>
        public string Latitud
        {
            set
            {
                if (value.Contains(".") == true)
                    value = value.Remove(value.IndexOf('.')) + value.Substring(value.IndexOf('.') + 1);
                if (value.Contains(",") == true)
                    value = value.Remove(value.IndexOf(',')) + value.Substring(value.IndexOf(',') + 1);
                //_Latitud = value.PadRight(8, '1');
                _Latitud = value.PadRight(8,'0');
            }
        }

        /// <summary>
        /// Valor de Longitud de Movil - OBLIGATORIO
        /// EEEEEEEEE - Longitud
        /// </summary>
        public string Longitud
        {
            set
            {
                if (value.Contains(".") == true)
                    value = value.Remove(value.IndexOf('.')) + value.Substring(value.IndexOf('.') + 1);
                if (value.Contains(",") == true)
                    value = value.Remove(value.IndexOf(',')) + value.Substring(value.IndexOf(',') + 1);

                _Longitud = value.PadRight(9, '0'); 
                //_Longitud = value.PadRight(9, '1'); 
            }
        }

        /// <summary>
        /// Velocidad de Movil - OPCIONAL
        /// </summary>
        public string Velocidad 
        { 
             set 
             {
                 if (value.Contains(".") == true)
                     value = value.Remove(value.IndexOf('.'));
                 if (value.Contains(",") == true)
                     value = value.Remove(value.IndexOf(','));

                 _Velocidad = value.PadLeft(3, '0').Substring(0, 3);  
             } 
        }
        /// <summary>
        /// Numero de Reporte - OPCIONAL
        /// </summary>
        public string Reporte
        {
            set
            {
                if (value.Contains(".") == true)
                    value = value.Remove(value.IndexOf('.'));
                if (value.Contains(",") == true)
                    value = value.Remove(value.IndexOf(','));

                _Reporte = value.PadLeft(2, '0').Substring(0, 2);
            }
        }
        /// <summary>
        /// Rumbo de Móvil - OPCIONAL
        /// </summary>
        public string Rumbo
        {
            set
            {
                if (value.Contains(".") == true)
                    value = value.Remove(value.IndexOf('.'));
                if (value.Contains(",") == true)
                    value = value.Remove(value.IndexOf(','));

                _Rumbo = value.PadLeft(3, '0').Substring(0, 3); ;
            }
        }
        /// <summary>
        /// Estado Entradas Digitales de Coyote, por defecto 0xFF - OPCIONAL
        /// </summary>
        public string EstadoEntradasDigitales
        {
            set
            {
                int qty = 255;
                if (Int32.TryParse(value, out qty) == false)
                {
                    Debug.WriteLine("EstadoEntradasDigitales no es un número válido (" + qty.ToString() + "), se setea a '0'");
                    qty = 255;
                }
                else if (qty < 0 || qty > 256)
                {
                    Debug.WriteLine("EstadoEntradasDigitales está fuera del rango válido (" + qty.ToString() + "), se setea a '0'");
                    qty = 255;
                }

                _EstadoEntradasDigitales = qty.ToString("X2").PadLeft(2, '0');
            }
        }
        /// <summary>
        /// Estado Salidas Digitales de Coyote, por defecto 0x00 - OPCIONAL
        /// </summary>
        public string EstadoSalidasDigitales
        {
            set
            {
                int qty = 0;
                if (Int32.TryParse(value, out qty) == false)
                {
                    Debug.WriteLine("EstadoSalidasDigitales no es un número válido (" + qty.ToString() + "), se setea a '0'");
                    qty = 0;
                }
                else if (qty < 0 || qty > 256)
                {
                    Debug.WriteLine("EstadoSalidasDigitales está fuera del rango válido (" + qty.ToString() + "), se setea a '0'");
                    qty = 0;
                }

                _EstadoSalidasDigitales = qty.ToString("X2").PadLeft(2, '0');
            }
        }
        /// <summary>
        /// Tension de Bateria, en decimos de Volt - OPCIONAL
        /// </summary>
        public string TensionDeBateria { set { _TensionDeBateria = value; } }
        /// <summary>
        /// Odómetro, en decimos de Volt - OPCIONAL
        /// </summary>
        public string Odometro { set { _Odometro = value; } }
        /// <summary>
        /// Estado PowerGPS, por defecto '1' - OPCIONAL
        /// </summary>
        public string EstadoPowerGPS
        {
            set
            {
                int status = 0;
                if (Int32.TryParse(value, out status) == false)
                {
                    Debug.WriteLine("EstadoPowerGPS no es un número válido (" + status.ToString() + "), se setea a '1'");
                    status = 1;
                }
                else if (status < 0 || status > 1)
                {
                    Debug.WriteLine("EstadoPowerGPS está fuera del rango válido (" + status.ToString() + "), se setea a '1'");
                    status = 1;
                }

                _EstadoPowerGPS = status.ToString();
            }
        }
        /// <summary>
        /// Estado PowerModem, por defecto '1' - OPCIONAL
        /// </summary>
        public string EstadoPowerModemCelular
        {
            set
            {
                int status = 0;
                if (Int32.TryParse(value, out status) == false)
                {
                    Debug.WriteLine("EstadoPowerModemCelular no es un número válido (" + status.ToString() + "), se setea a '1'");
                    status = 1;
                }
                else if (status < 0 || status > 1)
                {
                    Debug.WriteLine("EstadoPowerModemCelular está fuera del rango válido (" + status.ToString() + "), se setea a '1'");
                    status = 1;
                }

                _EstadoPowerModemCelular = status.ToString();
            }
        }
        /// <summary>
        /// Estado Registro GSM, por defecto '1' - OPCIONAL
        ///
        /// Valores posibles:
        /// 0=Not registered, ME is not currently searching a new operador to register to
        /// 1=Registered, home network
        /// 2=Not registered, but ME is currently searching a new operator to register to
        /// 3=Registration denied
        /// 4=Unknown
        /// 5=Registered, roaming
        /// </summary>
        public string EstadoRegistroGSM
        {
            set
            {
                int status = 0;
                if (Int32.TryParse(value, out status) == false)
                {
                    Debug.WriteLine("EstadoRegistroGSM no es un número válido (" + status.ToString() + "), se setea a '1'");
                    status = 1;
                }
                else if (status < 0 || status > 5)
                {
                    Debug.WriteLine("EstadoRegistroGSM está fuera del rango válido (" + status.ToString() + "), se setea a '1'");
                    status = 1;
                }

                _EstadoRegistroGSM = status.ToString();
            }
        }
        /// <summary>
        /// Modo GPSFix - Por defecto '2' - OPCIONAL
        /// 2=2D FIX
        /// 3=3D FIX
        /// </summary>
        public string GPSFixMode
        {
            set
            {
                int mode = 0;
                if (Int32.TryParse(value, out mode) == false)
                {
                    Debug.WriteLine("GPSFixMode no es un número válido (" + mode.ToString() + "), se setea a '2'");
                    mode = 2;
                }
                else if (mode < 2 || mode > 3)
                {
                    Debug.WriteLine("GPSFixMode está fuera del rango válido (" + mode.ToString() + "), se setea a '2'");
                    mode = 2;
                }

                _GPSFixMode = mode.ToString();
            }
        }
        /// <summary>
        /// Parte entera en decimal. Rango válido de 0 a 50 inclusive. Por defecto 0 - OPCIONAL
        /// </summary>
        public string PDOP
        {
            set
            {
                int mode = 0;
                if (Int32.TryParse(value, out mode) == false)
                {
                    Debug.WriteLine("PDOP no es un número válido (" + mode.ToString() + "), se setea a '0'");
                    mode = 0;
                }
                else if (mode < 0 || mode > 50)
                {
                    Debug.WriteLine("PDOP está fuera del rango válido (" + mode.ToString() + "), se setea a '0'");
                    mode = 0;
                }

                _PDOP = mode.ToString().PadLeft(2, '0');
            }
        }
        /// <summary>
        /// Cantidad de Satelites GPS disponibles. Rango válido de 3 a 24 inclusive. Por defecto 3 - OPCIONAL
        /// </summary>
        public string SatelitesDisponibles
        {
            set
            {
                int qty = 0;
                if (Int32.TryParse(value, out qty) == false)
                {
                    Debug.WriteLine("SatelitesDisponibles no es un número válido (" + qty.ToString() + "), se setea a '3'");
                    qty = 3;
                }
                else if (qty < 3 || qty > 24)
                {
                    Debug.WriteLine("SatelitesDisponibles está fuera del rango válido (" + qty.ToString() + "), se setea a '3'");
                    qty = 3;
                }

                _SatelitesDisponibles = qty.ToString().PadLeft(2, '0');
            }
        }
        /// <summary>
        /// Cantidad de segundos desde ultima posición OK - Por defecto 0 - OPCIONAL
        /// </summary>
        public string SegundosUltimaPosicionOk
        {
            set
            {
                int qty = 0;
                if (Int32.TryParse(value, out qty) == false)
                {
                    Debug.WriteLine("SegundosUltimaPosicionOk no es un número válido (" + qty.ToString() + "), se setea a '0'");
                    qty = 0;
                }
                else if (qty < 0 || qty > 9999)
                {
                    Debug.WriteLine("SegundosUltimaPosicionOk está fuera del rango válido (" + qty.ToString() + "), se setea a '0'");
                    qty = 0;
                }

                _SegundosUltimaPosicionOk = qty.ToString().PadLeft(4, '0');
            }
        }
        /// <summary>
        /// Cantidad de señal en dBm. Rango válido de 0 a 32 - Por defecto 0 - OPCIONAL
        /// </summary>
        public string NivelDeSeñal
        {
            set
            {
                int qty = 0;
                if (Int32.TryParse(value, out qty) == false)
                {
                    Debug.WriteLine("NivelDeSeñal no es un número válido (" + qty.ToString() + "), se setea a '0'");
                    qty = 0;
                }
                else if (qty < 0 || qty > 32)
                {
                    Debug.WriteLine("NivelDeSeñal está fuera del rango válido (" + qty.ToString() + "), se setea a '0'");
                    qty = 0;
                }

                _NivelDeSeñal = qty.ToString().PadLeft(2, '0');
            }
        }

#if DEBUG == true
        public DateTime FechaHora
        {
            set
            {
                _FechaHora = value;
            }
        }
#endif
        #endregion


    }
}
